﻿
namespace Patterns4Net.ArchitectureExplorer.Diagram
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Base class for all elements in diagram.
    /// </summary>
    public abstract class DiagramElement
    {
        private IDictionary<Type, object> tags = new Dictionary<Type, object>();

        protected DiagramElement(string name)
        {
            this.Name = name;
        }

        public DiagramElement Parent { get; internal set; }

        public string Name { get; private set; }

        public void AddTag(object tag)
        {
            object existing;
            if (this.tags.TryGetValue(tag.GetType(), out existing))
            {
                throw new TagWithTheTypeWasAlreadyAssigned(tag.GetType().Name, this);
            }

            this.tags.Add(tag.GetType(), tag);
        }

        public bool TryGetTag<T>(out T result)
        {
            result = default(T);
            object objResult;
            var found = this.tags.TryGetValue(typeof(T), out objResult);
            if (found)
            {
                result = (T)objResult;
            }

            return found;
        }

        public T GetTag<T>()
        {
            T result;
            this.TryGetTag(out result);
            return result;
        }

        public abstract void Accept(DiagramVisitor visitor);

        public override string ToString()
        {
            return this.Name;
        }

        public class TagWithTheTypeWasAlreadyAssigned : ApplicationException
        {
            public TagWithTheTypeWasAlreadyAssigned(string typeName, DiagramElement parent)
                : base(string.Format("Tag with the type {0} was already assigned in DiagramElement of type {1} ({2}).", typeName, parent.GetType().Name, parent))
            {
                this.TypeName = typeName;
                this.DiagramElement = parent;
            }

            public string TypeName { get; private set; }

            public DiagramElement DiagramElement { get; private set; }
        }
    }
}
